////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2010.
// -------------------------------------------------------------------------
//  File name:   ParticleSubEmitter.h
//  Version:     v1.00
//  Created:     20/04/2010 by Corey.
//  Description: Split out from ParticleEmitter.h
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#ifndef __particlesubemitter_h__
#define __particlesubemitter_h__
#pragma once

#include "IParticles.h"
#include "ParticleContainer.h"

class CParticle;
class CParticleContainer;
class CParticleEmitter;
class CParticleSubEmitter;
struct SParticleUpdateContext;

//////////////////////////////////////////////////////////////////////////
struct STimeState
{
  float				m_fAge;							// Age since birth.
	float				m_fEndAge;					// Age of death.
	float				m_fCollideAge;			// Age of first collision (for SpawnOnCollision children).

	STimeState()
		: m_fAge(0.f), m_fEndAge(0.f), m_fCollideAge(fHUGE) {}

	ILINE float GetAge() const
		{ return m_fAge; }
	ILINE float GetEndAge() const
		{ return m_fEndAge; }
	float GetCollideAge() const
		{ return m_fCollideAge; }

	ILINE float GetRelativeAge( float fAgeAdjust = 0.f ) const
	{
		float fRelativeAge = div_min(max(m_fAge + fAgeAdjust, 0.f), m_fEndAge, 1.f);
		assert(fRelativeAge >= 0.f && fRelativeAge <= 1.f);
		return fRelativeAge;		
	}
	ILINE bool IsAlive( float fTimeAdjust = 0.f ) const
		{ return m_fAge+fTimeAdjust < m_fEndAge; }

	void Start( float fAge = 0.f )
	{ 
		// Make sure emission active.
		m_fAge = min(m_fAge, -fAge); 
		m_fEndAge = m_fCollideAge = fHUGE;
	}
	void End( float fTimeAdjust = 0.f )
		// Stop emission at current time.
		{ m_fEndAge = min(m_fAge+fTimeAdjust, m_fEndAge); }
	void Collide( float fTime )
		{ m_fCollideAge = min(m_fCollideAge, fTime); }
	void Kill()
		// Set dead in the distant past.
		{ End(-fHUGE); }
};

struct SParticleMoveState: STimeState
{
	QuatTS			m_Loc;								// Position, orientation, and size.
	Velocity3		m_Vel;								// Linear and rotational velocity.

	SParticleMoveState()
		: m_Loc(IDENTITY), m_Vel(ZERO) {}

	QuatTS const& GetLocation() const
		{ return m_Loc; }
	Matrix34 GetMatrix() const
		{ return Matrix34(m_Loc); }
	Vec3 GetWorldPosition( Vec3 const& relpos ) const
		{ return m_Loc * relpos; }
	Vec3 GetWorldOffset( Vec3 const& relpos ) const
		{ return m_Loc.q * relpos * m_Loc.s; }

	void SetLocation( QuatTS const& loc )
		{ m_Loc = loc; }

	Velocity3 const& GetVelocity() const
		{ return m_Vel; }
	Velocity3& GetVelocity()
		{ return m_Vel; }
};

//////////////////////////////////////////////////////////////////////////
class CParticleSource: public SParticleMoveState, public Cry3DEngineBase
{
public:

	CParticleSource();
	~CParticleSource();

	void SetEmitter( const CParticleSubEmitter* pParent )
		{ m_pEmitter = pParent; }
	const CParticleSubEmitter* GetEmitter() const
		{ return m_pEmitter; }

	// Geom attachment.
	GeomRef const& GetEmitGeom() const
		{ return m_EmitGeom; }
	bool SetEmitGeom( GeomRef geom );

	void OffsetPosition(const Vec3 &delta)
	{
#ifdef SEG_WORLD
		m_Loc.t += delta;
#endif
	}

protected:

	const CParticleSubEmitter*	m_pEmitter;							// Parent emitter, if this is a child emitter.
	GeomRef											m_EmitGeom;							// Geometry shape for particle emission.
} _ALIGN(16);

//////////////////////////////////////////////////////////////////////////
// struct to group a batch of emitters together for SPU update
struct UpdateParticlesEmitterBatch
{		
	// don't incrase this to much, since it is passed by value to the spu,
	// where a maximum of 80 bytes is allowed
#if defined(WIN64) // due size contraints of the parameter structure use fewer objects in the batch
	static const int MAX_BATCHES = 6;
#else
	static const int MAX_BATCHES = 15;
#endif
	CParticleEmitter *batch[MAX_BATCHES];
};

//////////////////////////////////////////////////////////////////////////
class CParticleSubEmitter: public Cry3DEngineBase
// Maintains an emitter source state, emits particles to a container
{
public:

	CParticleSubEmitter( CParticleSubEmitter* pParent, CParticleSource* pSource, CParticleContainer* pCont );
	~CParticleSubEmitter();

	ResourceParticleParams const& GetParams() const	
		{ return m_pContainer->GetParams(); }
	CParticleContainer& GetContainer() const
		{ return *m_pContainer; }
	CParticleSource& GetSource() const
		{ return *m_pSource; }
	const CParticleSubEmitter* GetParent() const
		{ return m_pParentEmitter ? m_pParentEmitter : m_pSource->GetEmitter(); }
	bool GetLocalTarget( ParticleTarget& target ) const;
	CParticleEmitter const& GetMain() const
		{ return GetContainer().GetMain(); }

	// State.

	// Emitter is valid if 1st-gen, or 2nd-gen and Source still valid.
	bool IsAlive() const
		{ return !m_pContainer->GetParent() || m_pSource->GetEmitter(); }

	// Timing.
	float GetAge() const
		{ return GetSource().GetAge(); }
	float GetStartAge() const
		{ return m_fStartAge; }
	float GetRepeatAge() const
		{ return m_fRepeatAge; }
	float GetEndAge() const
		{ return m_fEndAge; }
	float GetParticleEndAge() const
		{ return GetEndAge() + GetParams().GetMaxParticleLife(); }
	float GetRelativeAge( float fAgeAdjust = 0.f) const
		{ return GetAgeRelativeTo( m_fEndAge, fAgeAdjust ); }

	float GetEndAge( ESoundControlTime eControl ) const;
	float GetRelativeAge( ESoundControlTime const eControl, float fAgeAdjust = 0.f ) const
		{ return GetAgeRelativeTo( min(GetEndAge(eControl), m_fRepeatAge), fAgeAdjust ); }

	float GetStrength( float fAgeAdjust = 0.f, ESoundControlTime const eControl = SoundControlTime_EmitterLifeTime ) const;
	CChaosKey GetChaosKey() const
		{ return m_ChaosKey; }
	uint32 GetEmitIndex() const
		{ return m_nEmitIndex; }
	uint16 GetSequence() const
		{ return m_nSequenceIndex; }

	Vec3 GetEmitPos() const;
	Vec3 GetLocalPivot() const;
	Vec3 GetWorldPivot() const;
	PivotLocation GetPivotLocation() const
	{
		return PivotLocation(GetSource().GetLocation(), GetLocalPivot(), eCoord_Local);
	}

	// Actions.
	void UpdateState();
	void UpdateSound();
	void ResetLoc()
		{ m_qpLastLoc.s = -1.f; }
	void SetLastLoc();
	bool GetMoveRelative(QuatTS& qtMove) const;
	void UpdateForce();

	bool HasForce() const
		{ return (m_pForce != 0); }

	int EmitParticle( SParticleUpdateContext const& context, const float fAge, const EmitParticleData* pData = 0 );
	void EmitParticles( SParticleUpdateContext const& context );
	
	void GetMemoryUsage( ICrySizer *pSizer ) const
		{}

	void OffsetPosition(const Vec3 &delta)
	{
#ifdef SEG_WORLD
		m_qpLastLoc.t += delta;
		if(m_pSource)
			m_pSource->OffsetPosition(delta);
#endif
	}

private:

	// Associated structures.
	CParticleContainer*							m_pContainer;					// Direct or shared container to emit particles into.
	CParticleSubEmitter*						m_pParentEmitter;
	CParticleSource*								m_pSource;

	// State.
	float					m_fStartAge;			// Relative age when scheduled to start (default 0).
	float					m_fEndAge;				// Relative age when scheduled to end (fHUGE if never).
	float					m_fRepeatAge;			// Relative age when scheduled to repeat (fHUGE if never).
	float					m_fNextEmitAge;		// Age to emit next particle.
	float					m_fLastUpdateAge;	// Cached age to detect reactivation.

	CChaosKey			m_ChaosKey;				// Seed for randomising; inited every pulse.
	PivotLocation	m_qpLastLoc;			// Location at time of last update.

	uint32				m_nEmitIndex;
	uint16				m_nSequenceIndex;

	// External objects.
	bool								m_bOneShotSoundGotStarted;
	tSoundID						m_SoundID;
	float								m_fSoundRangeSq;	// Cached sound radius, to avoid playing far sounds.
	IPhysicalEntity*		m_pForce;

	// Methods.
	void Start( float fAge );
	bool SoundInRange() const;

	float GetAgeRelativeTo( float fEndAge, float fAgeAdjust = 0.f) const;

} _ALIGN(16);

#endif // __particlesubemitter_h__
